{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"10_Utilities","provenance":[{"file_id":"https://github.com/GokuMohandas/MadeWithML/blob/main/notebooks/10_Utilities.ipynb","timestamp":1608073275916},{"file_id":"https://github.com/GokuMohandas/MadeWithML/blob/main/notebooks/10_Utilities.ipynb","timestamp":1583210267430}],"collapsed_sections":[],"toc_visible":true},"kernelspec":{"name":"python3","display_name":"Python 3"},"accelerator":"GPU"},"cells":[{"cell_type":"markdown","metadata":{"id":"WX2WO7zz4H8q"},"source":["<div align=\"center\">\n","<h1><img width=\"30\" src=\"https://madewithml.com/static/images/rounded_logo.png\">&nbsp;<a href=\"https://madewithml.com/\">Made With ML</a></h1>\n","Applied ML · MLOps · Production\n","<br>\n","Join 30K+ developers in learning how to responsibly <a href=\"https://madewithml.com/about/\">deliver value</a> with ML.\n","    <br>\n","</div>\n","\n","<br>\n","\n","<div align=\"center\">\n","    <a target=\"_blank\" href=\"https://newsletter.madewithml.com\"><img src=\"https://img.shields.io/badge/Subscribe-30K-brightgreen\"></a>&nbsp;\n","    <a target=\"_blank\" href=\"https://github.com/GokuMohandas/MadeWithML\"><img src=\"https://img.shields.io/github/stars/GokuMohandas/MadeWithML.svg?style=social&label=Star\"></a>&nbsp;\n","    <a target=\"_blank\" href=\"https://www.linkedin.com/in/goku\"><img src=\"https://img.shields.io/badge/style--5eba00.svg?label=LinkedIn&logo=linkedin&style=social\"></a>&nbsp;\n","    <a target=\"_blank\" href=\"https://twitter.com/GokuMohandas\"><img src=\"https://img.shields.io/twitter/follow/GokuMohandas.svg?label=Follow&style=social\"></a>\n","    <br>\n","    🔥&nbsp; Among the <a href=\"https://github.com/topics/deep-learning\" target=\"_blank\">top ML</a> repositories on GitHub\n","</div>\n","\n","<br>\n","<hr>"]},{"cell_type":"markdown","metadata":{"id":"eTdCMVl9YAXw"},"source":["# Utilities\n","In this lesson, we will explore utilities to extend and simplify training."]},{"cell_type":"markdown","metadata":{"id":"xuabAj4PYj57"},"source":["<div align=\"left\">\n","<a target=\"_blank\" href=\"https://madewithml.com/courses/basics/utilities/\"><img src=\"https://img.shields.io/badge/📖 Read-blog post-9cf\"></a>&nbsp;\n","<a href=\"https://github.com/GokuMohandas/MadeWithML/blob/main/notebooks/10_Utilities.ipynb\" role=\"button\"><img src=\"https://img.shields.io/static/v1?label=&amp;message=View%20On%20GitHub&amp;color=586069&amp;logo=github&amp;labelColor=2f363d\"></a>&nbsp;\n","<a href=\"https://colab.research.google.com/github/GokuMohandas/MadeWithML/blob/main/notebooks/10_Utilities.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"></a>\n","</div>"]},{"cell_type":"markdown","metadata":{"id":"XJhSbs58DJ6J"},"source":["# Set up"]},{"cell_type":"markdown","metadata":{"id":"1Y4B-vV4WuxR"},"source":["We're having to set a lot of seeds for reproducibility now, so let's wrap it all up in a function."]},{"cell_type":"code","metadata":{"id":"bU6W3WS_E12l"},"source":["import numpy as np\n","import pandas as pd\n","import random\n","import torch\n","import torch.nn as nn"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"nN1UNo6HE23b"},"source":["SEED = 1234"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"sLLG-r61E2_M"},"source":["def set_seeds(seed=1234):\n","    \"\"\"Set seeds for reproducibility.\"\"\"\n","    np.random.seed(seed)\n","    random.seed(seed)\n","    torch.manual_seed(seed)\n","    torch.cuda.manual_seed(seed)\n","    torch.cuda.manual_seed_all(seed) # multi-GPU"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"0uih55opr7ye"},"source":["# Set seeds for reproducibility\n","set_seeds(seed=SEED)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"6jiN0KrPr71U","executionInfo":{"status":"ok","timestamp":1608325908794,"user_tz":420,"elapsed":820,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"e3ee36ff-135a-438f-996b-f592dd9c80f2"},"source":["# Set device\n","cuda = True\n","device = torch.device('cuda' if (\n","    torch.cuda.is_available() and cuda) else 'cpu')\n","torch.set_default_tensor_type('torch.FloatTensor')\n","if device.type == 'cuda':\n","    torch.set_default_tensor_type('torch.cuda.FloatTensor')\n","print (device)"],"execution_count":null,"outputs":[{"output_type":"stream","text":["cuda\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"XtKqNioAayCy"},"source":["## Load data"]},{"cell_type":"markdown","metadata":{"id":"X3OrtMpFayFC"},"source":["We'll use the same spiral dataset from previous lessons to demonstrate our utilities."]},{"cell_type":"code","metadata":{"id":"9NfIz_4OPYpG"},"source":["import matplotlib.pyplot as plt\n","import pandas as pd"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"q14QCqKxUS2v","colab":{"base_uri":"https://localhost:8080/","height":204},"executionInfo":{"status":"ok","timestamp":1608245042164,"user_tz":420,"elapsed":1544,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"c64f6dde-cd92-4a44-e178-2acc8764c9d5"},"source":["# Load data\n","url = \"https://raw.githubusercontent.com/GokuMohandas/MadeWithML/main/datasets/spiral.csv\"\n","df = pd.read_csv(url, header=0) # load\n","df = df.sample(frac=1).reset_index(drop=True) # shuffle\n","df.head()"],"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/html":["<div>\n","<style scoped>\n","    .dataframe tbody tr th:only-of-type {\n","        vertical-align: middle;\n","    }\n","\n","    .dataframe tbody tr th {\n","        vertical-align: top;\n","    }\n","\n","    .dataframe thead th {\n","        text-align: right;\n","    }\n","</style>\n","<table border=\"1\" class=\"dataframe\">\n","  <thead>\n","    <tr style=\"text-align: right;\">\n","      <th></th>\n","      <th>X1</th>\n","      <th>X2</th>\n","      <th>color</th>\n","    </tr>\n","  </thead>\n","  <tbody>\n","    <tr>\n","      <th>0</th>\n","      <td>0.106737</td>\n","      <td>0.114197</td>\n","      <td>c1</td>\n","    </tr>\n","    <tr>\n","      <th>1</th>\n","      <td>0.311513</td>\n","      <td>-0.664028</td>\n","      <td>c1</td>\n","    </tr>\n","    <tr>\n","      <th>2</th>\n","      <td>0.019870</td>\n","      <td>-0.703126</td>\n","      <td>c1</td>\n","    </tr>\n","    <tr>\n","      <th>3</th>\n","      <td>-0.054017</td>\n","      <td>0.508159</td>\n","      <td>c3</td>\n","    </tr>\n","    <tr>\n","      <th>4</th>\n","      <td>-0.127751</td>\n","      <td>-0.011382</td>\n","      <td>c3</td>\n","    </tr>\n","  </tbody>\n","</table>\n","</div>"],"text/plain":["         X1        X2 color\n","0  0.106737  0.114197    c1\n","1  0.311513 -0.664028    c1\n","2  0.019870 -0.703126    c1\n","3 -0.054017  0.508159    c3\n","4 -0.127751 -0.011382    c3"]},"metadata":{"tags":[]},"execution_count":43}]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"uQ4uWiGciPBS","executionInfo":{"status":"ok","timestamp":1608245042165,"user_tz":420,"elapsed":1532,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"1a1f02a7-c3ad-4b58-f7fd-de5436aecba2"},"source":["# Data shapes\n","X = df[['X1', 'X2']].values\n","y = df['color'].values\n","print (\"X: \", np.shape(X))\n","print (\"y: \", np.shape(y))"],"execution_count":null,"outputs":[{"output_type":"stream","text":["X:  (1500, 2)\n","y:  (1500,)\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/","height":281},"id":"Sm601G5XiPEH","executionInfo":{"status":"ok","timestamp":1608245042166,"user_tz":420,"elapsed":1518,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"0bd578be-28f6-4379-80b2-53c0b3560518"},"source":["# Visualize data\n","plt.title(\"Generated non-linear data\")\n","colors = {'c1': 'red', 'c2': 'yellow', 'c3': 'blue'}\n","plt.scatter(X[:, 0], X[:, 1], c=[colors[_y] for _y in y], edgecolors='k', s=25)\n","plt.show()"],"execution_count":null,"outputs":[{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<Figure size 432x288 with 1 Axes>"]},"metadata":{"tags":[],"needs_background":"light"}}]},{"cell_type":"markdown","metadata":{"id":"0kEIDV2rWkPA"},"source":["## Split data"]},{"cell_type":"code","metadata":{"id":"VNHCAonhWpQL"},"source":["import collections\n","from sklearn.model_selection import train_test_split"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"fXVnk18FsaiO"},"source":["TRAIN_SIZE = 0.7\n","VAL_SIZE = 0.15\n","TEST_SIZE = 0.15"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"NSffHAoeQmBM"},"source":["def train_val_test_split(X, y, train_size):\n","    \"\"\"Split dataset into data splits.\"\"\"\n","    X_train, X_, y_train, y_ = train_test_split(X, y, train_size=TRAIN_SIZE, stratify=y)\n","    X_val, X_test, y_val, y_test = train_test_split(X_, y_, train_size=0.5, stratify=y_)\n","    return X_train, X_val, X_test, y_train, y_val, y_test"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"COyhVvqLz2Ze","executionInfo":{"status":"ok","timestamp":1608245042168,"user_tz":420,"elapsed":1482,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"94e2efd4-ac84-4bd4-cb06-4414b8991ee4"},"source":["# Create data splits\n","X_train, X_val, X_test, y_train, y_val, y_test = train_val_test_split(\n","    X=X, y=y, train_size=TRAIN_SIZE)\n","print (f\"X_train: {X_train.shape}, y_train: {y_train.shape}\")\n","print (f\"X_val: {X_val.shape}, y_val: {y_val.shape}\")\n","print (f\"X_test: {X_test.shape}, y_test: {y_test.shape}\")\n","print (f\"Sample point: {X_train[0]} → {y_train[0]}\")"],"execution_count":null,"outputs":[{"output_type":"stream","text":["X_train: (1050, 2), y_train: (1050,)\n","X_val: (225, 2), y_val: (225,)\n","X_test: (225, 2), y_test: (225,)\n","Sample point: [-0.63919105 -0.69724176] → c1\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"y8P2N42lZKW2"},"source":["## LabelEncoder"]},{"cell_type":"markdown","metadata":{"id":"b7Qw3ywlrcGp"},"source":["Next we'll define a `LabelEncoder` to encode our text labels into unique indices. We're not going to use scikit-learn's LabelEncoder anymore because we want to be able to save and load our instances the way we want to. "]},{"cell_type":"code","metadata":{"id":"GD8gd34w11IA"},"source":["import itertools"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"5bYN5NP1ZK0k"},"source":["class LabelEncoder(object):\n","    \"\"\"Label encoder for tag labels.\"\"\"\n","    def __init__(self, class_to_index={}):\n","        self.class_to_index = class_to_index\n","        self.index_to_class = {v: k for k, v in self.class_to_index.items()}\n","        self.classes = list(self.class_to_index.keys())\n","\n","    def __len__(self):\n","        return len(self.class_to_index)\n","\n","    def __str__(self):\n","        return f\"<LabelEncoder(num_classes={len(self)})>\"\n","\n","    def fit(self, y):\n","        classes = np.unique(y)\n","        for i, class_ in enumerate(classes):\n","            self.class_to_index[class_] = i\n","        self.index_to_class = {v: k for k, v in self.class_to_index.items()}\n","        self.classes = list(self.class_to_index.keys())\n","        return self\n","\n","    def encode(self, y):\n","        encoded = np.zeros((len(y)), dtype=int)\n","        for i, item in enumerate(y):\n","            encoded[i] = self.class_to_index[item]\n","        return encoded\n","\n","    def decode(self, y):\n","        classes = []\n","        for i, item in enumerate(y):\n","            classes.append(self.index_to_class[item])\n","        return classes\n","\n","    def save(self, fp):\n","        with open(fp, 'w') as fp:\n","            contents = {'class_to_index': self.class_to_index}\n","            json.dump(contents, fp, indent=4, sort_keys=False)\n","\n","    @classmethod\n","    def load(cls, fp):\n","        with open(fp, 'r') as fp:\n","            kwargs = json.load(fp=fp)\n","        return cls(**kwargs)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"CrWsgug6ZGLs","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1608245042170,"user_tz":420,"elapsed":1456,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"95fa1d11-b119-4e38-df90-b1a241382960"},"source":["# Encode\n","label_encoder = LabelEncoder()\n","label_encoder.fit(y_train)\n","label_encoder.class_to_index"],"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["{'c1': 0, 'c2': 1, 'c3': 2}"]},"metadata":{"tags":[]},"execution_count":52}]},{"cell_type":"code","metadata":{"id":"N8me9sECZZUL","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1608245042170,"user_tz":420,"elapsed":1443,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"bc808c0b-dc3b-4f26-cdfe-d646cbcbd4c8"},"source":["# Convert labels to tokens\n","print (f\"y_train[0]: {y_train[0]}\")\n","y_train = label_encoder.encode(y_train)\n","y_val = label_encoder.encode(y_val)\n","y_test = label_encoder.encode(y_test)\n","print (f\"y_train[0]: {y_train[0]}\")"],"execution_count":null,"outputs":[{"output_type":"stream","text":["y_train[0]: c1\n","y_train[0]: 0\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"id":"D3syfG98QSC1","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1608245042171,"user_tz":420,"elapsed":1430,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"1a104a16-fa13-4af8-9d52-1bdcc3bf83b3"},"source":["# Class weights\n","counts = np.bincount(y_train)\n","class_weights = {i: 1.0/count for i, count in enumerate(counts)}\n","print (f\"counts: {counts}\\nweights: {class_weights}\")"],"execution_count":null,"outputs":[{"output_type":"stream","text":["counts: [350 350 350]\n","weights: {0: 0.002857142857142857, 1: 0.002857142857142857, 2: 0.002857142857142857}\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"Jm172qNiimaj"},"source":["## StandardScaler"]},{"cell_type":"markdown","metadata":{"id":"-7cOXzFIipy8"},"source":["We need to standardize our data (zero mean and unit variance) so a specific feature's magnitude doesn't affect how the model learns its weights. We're only going to standardize the inputs X because our outputs y are class values. We're going to compose our own `StandardScaler` class so we have easily save and load it later during inference."]},{"cell_type":"code","metadata":{"id":"GydwSYTdXX3D"},"source":["class StandardScaler(object):\n","    def __init__(self, mean=None, std=None):\n","        self.mean = np.array(mean)\n","        self.std = np.array(std)\n","\n","    def fit(self, X):\n","        self.mean =  np.mean(X_train, axis=0)\n","        self.std = np.std(X_train, axis=0)\n","\n","    def scale(self, X):\n","        return (X - self.mean) / self.std\n","\n","    def unscale(self, X):\n","        return (X * self.std) + self.mean\n","\n","    def save(self, fp):\n","        with open(fp, 'w') as fp:\n","            contents = {'mean': self.mean.tolist(), 'std': self.std.tolist()}\n","            json.dump(contents, fp, indent=4, sort_keys=False)\n","\n","    @classmethod\n","    def load(cls, fp):\n","        with open(fp, 'r') as fp:\n","            kwargs = json.load(fp=fp)\n","        return cls(**kwargs)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"Xc9cETrtiqkm"},"source":["# Standardize the data (mean=0, std=1) using training data\n","X_scaler = StandardScaler()\n","X_scaler.fit(X_train)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"gQRt0ckuiqnp"},"source":["# Apply scaler on training and test data (don't standardize outputs for classification)\n","X_train = X_scaler.scale(X_train)\n","X_val = X_scaler.scale(X_val)\n","X_test = X_scaler.scale(X_test)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"GL7etRWritue","executionInfo":{"status":"ok","timestamp":1608245042491,"user_tz":420,"elapsed":1712,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"b45e9c87-4af9-4da1-e0d8-979b559b7778"},"source":["# Check (means should be ~0 and std should be ~1)\n","print (f\"X_test[0]: mean: {np.mean(X_test[:, 0], axis=0):.1f}, std: {np.std(X_test[:, 0], axis=0):.1f}\")\n","print (f\"X_test[1]: mean: {np.mean(X_test[:, 1], axis=0):.1f}, std: {np.std(X_test[:, 1], axis=0):.1f}\")"],"execution_count":null,"outputs":[{"output_type":"stream","text":["X_test[0]: mean: 0.1, std: 0.9\n","X_test[1]: mean: 0.0, std: 1.0\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"XwZmG3ZDCDdz"},"source":["# DataLoader"]},{"cell_type":"markdown","metadata":{"id":"lPIWpCsmCal3"},"source":["We're going to place our data into a [`Dataset`](https://pytorch.org/docs/stable/data.html#torch.utils.data.Dataset) and use a [`DataLoader`](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader) to efficiently create batches for training and evaluation."]},{"cell_type":"code","metadata":{"id":"UBsI7_0gS8CV"},"source":["import torch"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"MPUviMfhS8FG","executionInfo":{"status":"ok","timestamp":1608245042495,"user_tz":420,"elapsed":1695,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"3c3820ee-e08e-47f7-d494-7f34c2f08dc7"},"source":["# Seed seed for reproducibility\n","torch.manual_seed(SEED)"],"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["<torch._C.Generator at 0x7efd361cacc0>"]},"metadata":{"tags":[]},"execution_count":60}]},{"cell_type":"code","metadata":{"id":"fK7laA88Cas1"},"source":["class Dataset(torch.utils.data.Dataset):\n","    def __init__(self, X, y):\n","        self.X = X\n","        self.y = y\n","\n","    def __len__(self):\n","        return len(self.y)\n","\n","    def __str__(self):\n","        return f\"<Dataset(N={len(self)})>\"\n","\n","    def __getitem__(self, index):\n","        X = self.X[index]\n","        y = self.y[index]\n","        return [X, y]\n","\n","    def collate_fn(self, batch):\n","        \"\"\"Processing on a batch.\"\"\"\n","        # Get inputs\n","        batch = np.array(batch, dtype=object)\n","        X = batch[:, 0]\n","        y = np.stack(batch[:, 1], axis=0)\n","\n","        # Cast\n","        X = torch.FloatTensor(X.astype(np.float32))\n","        y = torch.LongTensor(y.astype(np.int32))\n","\n","        return X, y\n","\n","    def create_dataloader(self, batch_size, shuffle=False, drop_last=False):\n","        return torch.utils.data.DataLoader(\n","            dataset=self, batch_size=batch_size, collate_fn=self.collate_fn,\n","            shuffle=shuffle, drop_last=drop_last, pin_memory=True)"],"execution_count":null,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"-ZK1uK9GmyP1"},"source":["We don't really need the `collate_fn` here but we wanted to make it transparent because we will need it when we want to do specific processing on our batch (ex. padding). We'll be using a custom collate function in the next lesson."]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"bb835L2XCavW","executionInfo":{"status":"ok","timestamp":1608245042496,"user_tz":420,"elapsed":1674,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"62625b62-7a14-4e46-e93a-720ea85ca26c"},"source":["# Create datasets\n","train_dataset = Dataset(X=X_train, y=y_train)\n","val_dataset = Dataset(X=X_val, y=y_val)\n","test_dataset = Dataset(X=X_test, y=y_test)\n","print (\"Datasets:\\n\"\n","    f\"  Train dataset:{train_dataset.__str__()}\\n\"\n","    f\"  Val dataset: {val_dataset.__str__()}\\n\"\n","    f\"  Test dataset: {test_dataset.__str__()}\\n\"\n","    \"Sample point:\\n\"\n","    f\"  X: {train_dataset[0][0]}\\n\"\n","    f\"  y: {train_dataset[0][1]}\")"],"execution_count":null,"outputs":[{"output_type":"stream","text":["Datasets:\n","  Train dataset:<Dataset(N=1050)>\n","  Val dataset: <Dataset(N=225)>\n","  Test dataset: <Dataset(N=225)>\n","Sample point:\n","  X: [-1.47355106 -1.67417243]\n","  y: 0\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"Lv5_9eFooNfY"},"source":["So far, we used batch gradient descent to update our weights. This means that we calculated the gradients using the entire training dataset. We also could've updated our weights using stochastic gradient descent (SGD) where we pass in one training example one at a time. The current standard is mini-batch gradient descent, which strikes a balance between batch and stochastic GD, where we update the weights using a mini-batch of n (`BATCH_SIZE`) samples. This is where the `DataLoader` object comes in handy."]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"qQI7OLYDWvhP","executionInfo":{"status":"ok","timestamp":1608245042496,"user_tz":420,"elapsed":1662,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"a6c2fe3b-fdc2-449b-f746-a4ea466d02f1"},"source":["# Create dataloaders\n","batch_size = 64\n","train_dataloader = train_dataset.create_dataloader(batch_size=batch_size)\n","val_dataloader = val_dataset.create_dataloader(batch_size=batch_size)\n","test_dataloader = test_dataset.create_dataloader(batch_size=batch_size)\n","batch_X, batch_y = next(iter(train_dataloader))\n","print (\"Sample batch:\\n\"\n","    f\"  X: {list(batch_X.size())}\\n\"\n","    f\"  y: {list(batch_y.size())}\\n\"\n","    \"Sample point:\\n\"\n","    f\"  X: {batch_X[0]}\\n\"\n","    f\"  y: {batch_y[0]}\")"],"execution_count":null,"outputs":[{"output_type":"stream","text":["Sample batch:\n","  X: [64, 2]\n","  y: [64]\n","Sample point:\n","  X: tensor([-1.4736, -1.6742])\n","  y: 0\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"4yOIyrd3CbpZ"},"source":["# Device"]},{"cell_type":"markdown","metadata":{"id":"Z70PtllpCfak"},"source":["So far we've been running our operations on the CPU but when we have large datasets and larger models to train, we can benefit by parallelzing tensor operations on a GPU. In this notebook, you can use a GPU by going to `Runtime` > `Change runtime type` > Select `GPU` in the `Hardware accelerator` dropdown. We can what device we're using with the following line of code:"]},{"cell_type":"code","metadata":{"id":"335nEZlJbcda"},"source":["# Set CUDA seeds\n","torch.cuda.manual_seed(SEED)\n","torch.cuda.manual_seed_all(SEED) # multi-GPU"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"rE9BJAyxCfe4","executionInfo":{"status":"ok","timestamp":1608245042497,"user_tz":420,"elapsed":1637,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"fca0386a-9558-4ea5-9a52-87251406e858"},"source":["# Set device\n","cuda = True\n","device = torch.device('cuda' if (\n","    torch.cuda.is_available() and cuda) else 'cpu')\n","torch.set_default_tensor_type('torch.FloatTensor')\n","if device.type == 'cuda':\n","    torch.set_default_tensor_type('torch.cuda.FloatTensor')\n","print (device)"],"execution_count":null,"outputs":[{"output_type":"stream","text":["cuda\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"3NWFcZnMCf6N"},"source":["# Model"]},{"cell_type":"markdown","metadata":{"id":"tAASSRx5ChMn"},"source":["Let's initialize the model we'll be using to show the capabilities of training utilities."]},{"cell_type":"code","metadata":{"id":"o_89ZatDChRt"},"source":["import math\n","from torch import nn\n","import torch.nn.functional as F"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"4KpTkA1_ChUI"},"source":["INPUT_DIM = X_train.shape[1] # 2D\n","HIDDEN_DIM = 100\n","DROPOUT_P = 0.1\n","NUM_CLASSES = len(label_encoder.classes)\n","NUM_EPOCHS = 10"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"wKfNogAAChWp"},"source":["class MLP(nn.Module):\n","    def __init__(self, input_dim, hidden_dim, dropout_p, num_classes):\n","        super(MLP, self).__init__()\n","        self.fc1 = nn.Linear(input_dim, hidden_dim)\n","        self.dropout = nn.Dropout(dropout_p)\n","        self.fc2 = nn.Linear(hidden_dim, num_classes)\n","        \n","    def forward(self, inputs, apply_softmax=False):\n","        x_in, = inputs\n","        z = F.relu(self.fc1(x_in))\n","        z = self.dropout(z)\n","        y_pred = self.fc2(z)\n","        if apply_softmax:\n","            y_pred = F.softmax(y_pred, dim=1) \n","        return y_pred"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"HBLos7GmCjim","executionInfo":{"status":"ok","timestamp":1608245042499,"user_tz":420,"elapsed":1597,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"ffc1d334-959d-46fe-95b4-34c1b08de112"},"source":["# Initialize model\n","model = MLP(\n","    input_dim=INPUT_DIM, hidden_dim=HIDDEN_DIM, \n","    dropout_p=DROPOUT_P, num_classes=NUM_CLASSES)\n","model = model.to(device) # set device\n","print (model.named_parameters)"],"execution_count":null,"outputs":[{"output_type":"stream","text":["<bound method Module.named_parameters of MLP(\n","  (fc1): Linear(in_features=2, out_features=100, bias=True)\n","  (dropout): Dropout(p=0.1, inplace=False)\n","  (fc2): Linear(in_features=100, out_features=3, bias=True)\n",")>\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"Ta8VwFtHCyxB"},"source":["# Trainer"]},{"cell_type":"markdown","metadata":{"id":"rz8oRlBsCqmV"},"source":["So far we've been writing training loops that train only using the train data split and then we perform evaluation on our test set. But in reality, we would follow this process:\n","\n","1. Train using mini-batches on one epoch of the train data split.\n","2. Evaluate loss on the validation split and use it to adjust hyperparameters (ex. learning rate).\n","3. After training ends (via stagnation in improvements, desired performance, etc.), evaluate your trained model on the test (hold-out) data split.\n","\n","We'll create a `Trainer` class to keep all of these processes organized."]},{"cell_type":"markdown","metadata":{"id":"sRxiuswlcOTQ"},"source":["The first function in the class is `train_step` which will train the model using batches from one epoch of the train data split.\n","\n","```python\n","def train_step(self, dataloader):\n","    \"\"\"Train step.\"\"\"\n","    # Set model to train mode\n","    self.model.train()\n","    loss = 0.0\n","\n","    # Iterate over train batches\n","    for i, batch in enumerate(dataloader):\n","\n","        # Step\n","        batch = [item.to(self.device) for item in batch]  # Set device\n","        inputs, targets = batch[:-1], batch[-1]\n","        self.optimizer.zero_grad()  # Reset gradients\n","        z = self.model(inputs)  # Forward pass\n","        J = self.loss_fn(z, targets)  # Define loss\n","        J.backward()  # Backward pass\n","        self.optimizer.step()  # Update weights\n","\n","        # Cumulative Metrics\n","        loss += (J.detach().item() - loss) / (i + 1)\n","\n","    return loss\n","```"]},{"cell_type":"markdown","metadata":{"id":"G6aiXVzscISY"},"source":["Next we'll define the `eval_step` which will be used for processing both the validation and test data splits. This is because neither of them require gradient updates and display the same metrics.\n","\n","```python\n","def eval_step(self, dataloader):\n","    \"\"\"Validation or test step.\"\"\"\n","    # Set model to eval mode\n","    self.model.eval()\n","    loss = 0.0\n","    y_trues, y_probs = [], []\n","\n","    # Iterate over val batches\n","    with torch.no_grad():\n","        for i, batch in enumerate(dataloader):\n","\n","            # Step\n","            batch = [item.to(self.device) for item in batch]  # Set device\n","            inputs, y_true = batch[:-1], batch[-1]\n","            z = self.model(inputs)  # Forward pass\n","            J = self.loss_fn(z, y_true).item()\n","\n","            # Cumulative Metrics\n","            loss += (J - loss) / (i + 1)\n","\n","            # Store outputs\n","            y_prob = torch.sigmoid(z).cpu().numpy()\n","            y_probs.extend(y_prob)\n","            y_trues.extend(y_true.cpu().numpy())\n","\n","    return loss, np.vstack(y_trues), np.vstack(y_probs)\n","```"]},{"cell_type":"markdown","metadata":{"id":"6RNrt41-cIZy"},"source":["The final function is the `predict_step` which will be used for inference. It's fairly similar to the `eval_step` except we don't calculate any metrics. We pass on the predictions which we can use to generate our performance report.\n","\n","```python\n","def predict_step(self, dataloader):\n","    \"\"\"Prediction step.\"\"\"\n","    # Set model to eval mode\n","    self.model.eval()\n","    y_probs = []\n","\n","    # Iterate over val batches\n","    with torch.no_grad():\n","        for i, batch in enumerate(dataloader):\n","\n","            # Forward pass w/ inputs\n","            inputs, targets = batch[:-1], batch[-1]\n","            y_prob = self.model(inputs, apply_softmax=True)\n","\n","            # Store outputs\n","            y_probs.extend(y_prob)\n","\n","    return np.vstack(y_probs)\n","```"]},{"cell_type":"markdown","metadata":{"id":"3IFLbT_vCjtB"},"source":["# LR Scheduler"]},{"cell_type":"markdown","metadata":{"id":"r03ot_PfClKZ"},"source":["As our model starts to optimize and perform better, the loss will reduce and we'll need to make smaller adjustments. If we keep using a fixed learning rate, we'll be overshooting back and forth. Therefore, we're going to add a learning rate scheduler to our optimizer to adjust our learning rate during training. There are many [schedulers](https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate) schedulers to choose from but a popular one is `ReduceLROnPlateau` which reduces the learning rate when a metric (ex. validation loss) stops improving. In the example below we'll reduce the learning rate by a factor of 0.1 (`factor=0.1`) when our metric of interest (`self.scheduler.step(val_loss)`) stops decreasing (`mode='min'`) for three (`patience=3`) straight epochs."]},{"cell_type":"markdown","metadata":{"id":"b3GOyMx9q2ED"},"source":["```python\n","# Initialize the LR scheduler\n","scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n","    optimizer, mode='min', factor=0.1, patience=3)\n","...\n","train_loop():\n","    ...\n","    # Steps\n","    train_loss = trainer.train_step(dataloader=train_dataloader)\n","    val_loss, _, _ = trainer.eval_step(dataloader=val_dataloader)\n","    self.scheduler.step(val_loss)\n","    ...\n","```"]},{"cell_type":"markdown","metadata":{"id":"9ERZO8YSCsuN"},"source":["# Early Stopping"]},{"cell_type":"markdown","metadata":{"id":"cxZZr0_2Cu1W"},"source":["We should never train our models for an arbitrary number of epochs but instead we should have explicit stopping criteria (even if you are bootstrapped by compute resources). Common stopping criteria include when validation performance stagnates for certain # of epochs (`patience`), desired performance is reached, etc."]},{"cell_type":"markdown","metadata":{"id":"lLgLQjJlssN-"},"source":["```python\n","# Early stopping\n","if val_loss < best_val_loss:\n","    best_val_loss = val_loss\n","    best_model = trainer.model\n","    _patience = patience  # reset _patience\n","else:\n","    _patience -= 1\n","if not _patience:  # 0\n","    print(\"Stopping early!\")\n","    break\n","```"]},{"cell_type":"markdown","metadata":{"id":"zOu2HFo1svkj"},"source":["# Training"]},{"cell_type":"markdown","metadata":{"id":"kIptNMAAsxP8"},"source":["Let's put all of this together now to train our model."]},{"cell_type":"code","metadata":{"id":"8fQCkJkDs5kC"},"source":["from torch.optim import Adam"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"m2dbieqks5mv"},"source":["LEARNING_RATE = 1e-2\n","NUM_EPOCHS = 100\n","PATIENCE = 3"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"0sU9D4-qs5sD"},"source":["# Define Loss\n","class_weights_tensor = torch.Tensor(list(class_weights.values())).to(device)\n","loss = nn.CrossEntropyLoss(weight=class_weights_tensor)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"SVB9PlTos-iZ"},"source":["# Define optimizer & scheduler\n","optimizer = Adam(model.parameters(), lr=LEARNING_RATE) \n","scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n","    optimizer, mode='min', factor=0.1, patience=3)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"2O0_Uk1tcE6s"},"source":["class Trainer(object):\n","    def __init__(self, model, device, loss_fn=None, optimizer=None, scheduler=None):\n","\n","        # Set params\n","        self.model = model\n","        self.device = device\n","        self.loss_fn = loss_fn\n","        self.optimizer = optimizer\n","        self.scheduler = scheduler\n","\n","    def train_step(self, dataloader):\n","        \"\"\"Train step.\"\"\"\n","        # Set model to train mode\n","        self.model.train()\n","        loss = 0.0\n","\n","        # Iterate over train batches\n","        for i, batch in enumerate(dataloader):\n","\n","            # Step\n","            batch = [item.to(self.device) for item in batch]  # Set device\n","            inputs, targets = batch[:-1], batch[-1]\n","            self.optimizer.zero_grad()  # Reset gradients\n","            z = self.model(inputs)  # Forward pass\n","            J = self.loss_fn(z, targets)  # Define loss\n","            J.backward()  # Backward pass\n","            self.optimizer.step()  # Update weights\n","\n","            # Cumulative Metrics\n","            loss += (J.detach().item() - loss) / (i + 1)\n","\n","        return loss\n","\n","    def eval_step(self, dataloader):\n","        \"\"\"Validation or test step.\"\"\"\n","        # Set model to eval mode\n","        self.model.eval()\n","        loss = 0.0\n","        y_trues, y_probs = [], []\n","\n","        # Iterate over val batches\n","        with torch.no_grad():\n","            for i, batch in enumerate(dataloader):\n","\n","                # Step\n","                batch = [item.to(self.device) for item in batch]  # Set device\n","                inputs, y_true = batch[:-1], batch[-1]\n","                z = self.model(inputs)  # Forward pass\n","                J = self.loss_fn(z, y_true).item()\n","\n","                # Cumulative Metrics\n","                loss += (J - loss) / (i + 1)\n","\n","                # Store outputs\n","                y_prob = torch.sigmoid(z).cpu().numpy()\n","                y_probs.extend(y_prob)\n","                y_trues.extend(y_true.cpu().numpy())\n","\n","        return loss, np.vstack(y_trues), np.vstack(y_probs)\n","\n","    def predict_step(self, dataloader):\n","        \"\"\"Prediction step.\"\"\"\n","        # Set model to eval mode\n","        self.model.eval()\n","        y_probs = []\n","\n","        # Iterate over val batches\n","        with torch.no_grad():\n","            for i, batch in enumerate(dataloader):\n","\n","                # Forward pass w/ inputs\n","                inputs, targets = batch[:-1], batch[-1]\n","                y_prob = self.model(inputs, apply_softmax=True)\n","\n","                # Store outputs\n","                y_probs.extend(y_prob)\n","\n","        return np.vstack(y_probs)\n","\n","    def train(self, num_epochs, patience, train_dataloader, val_dataloader):\n","        best_val_loss = np.inf\n","        for epoch in range(num_epochs):\n","            # Steps\n","            train_loss = self.train_step(dataloader=train_dataloader)\n","            val_loss, _, _ = self.eval_step(dataloader=val_dataloader)\n","            self.scheduler.step(val_loss)\n","\n","            # Early stopping\n","            if val_loss < best_val_loss:\n","                best_val_loss = val_loss\n","                best_model = self.model\n","                _patience = patience  # reset _patience\n","            else:\n","                _patience -= 1\n","            if not _patience:  # 0\n","                print(\"Stopping early!\")\n","                break\n","\n","            # Logging\n","            print(\n","                f\"Epoch: {epoch+1} | \"\n","                f\"train_loss: {train_loss:.5f}, \"\n","                f\"val_loss: {val_loss:.5f}, \"\n","                f\"lr: {self.optimizer.param_groups[0]['lr']:.2E}, \"\n","                f\"_patience: {_patience}\"\n","            )\n","        return best_model"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"ieWtfxfis5ug"},"source":["# Trainer module\n","trainer = Trainer(\n","    model=model, device=device, loss_fn=loss_fn, \n","    optimizer=optimizer, scheduler=scheduler)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"zuOVOqfjsxVH","executionInfo":{"status":"ok","timestamp":1608245044288,"user_tz":420,"elapsed":3324,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"6a0ce5b7-cb34-455f-f781-0f4987ef8003"},"source":["# Train\n","best_model = trainer.train(\n","    NUM_EPOCHS, PATIENCE, train_dataloader, val_dataloader)"],"execution_count":null,"outputs":[{"output_type":"stream","text":["Epoch: 1 | train_loss: 0.73999, val_loss: 0.58441, lr: 1.00E-02, _patience: 3\n","Epoch: 2 | train_loss: 0.52631, val_loss: 0.41542, lr: 1.00E-02, _patience: 3\n","Epoch: 3 | train_loss: 0.40919, val_loss: 0.30673, lr: 1.00E-02, _patience: 3\n","Epoch: 4 | train_loss: 0.31421, val_loss: 0.22428, lr: 1.00E-02, _patience: 3\n","Epoch: 5 | train_loss: 0.25295, val_loss: 0.17446, lr: 1.00E-02, _patience: 3\n","Epoch: 6 | train_loss: 0.22243, val_loss: 0.14668, lr: 1.00E-02, _patience: 3\n","Epoch: 7 | train_loss: 0.18416, val_loss: 0.12333, lr: 1.00E-02, _patience: 3\n","Epoch: 8 | train_loss: 0.16403, val_loss: 0.10400, lr: 1.00E-02, _patience: 3\n","Epoch: 9 | train_loss: 0.15296, val_loss: 0.09289, lr: 1.00E-02, _patience: 3\n","Epoch: 10 | train_loss: 0.14347, val_loss: 0.08267, lr: 1.00E-02, _patience: 3\n","Epoch: 11 | train_loss: 0.13843, val_loss: 0.07776, lr: 1.00E-02, _patience: 3\n","Epoch: 12 | train_loss: 0.11476, val_loss: 0.06921, lr: 1.00E-02, _patience: 3\n","Epoch: 13 | train_loss: 0.10692, val_loss: 0.06558, lr: 1.00E-02, _patience: 3\n","Epoch: 14 | train_loss: 0.10908, val_loss: 0.06009, lr: 1.00E-02, _patience: 3\n","Epoch: 15 | train_loss: 0.11100, val_loss: 0.05664, lr: 1.00E-02, _patience: 3\n","Epoch: 16 | train_loss: 0.09072, val_loss: 0.05101, lr: 1.00E-02, _patience: 3\n","Epoch: 17 | train_loss: 0.08836, val_loss: 0.04995, lr: 1.00E-02, _patience: 3\n","Epoch: 18 | train_loss: 0.08801, val_loss: 0.04807, lr: 1.00E-02, _patience: 3\n","Epoch: 19 | train_loss: 0.08597, val_loss: 0.04441, lr: 1.00E-02, _patience: 3\n","Epoch: 20 | train_loss: 0.07829, val_loss: 0.04125, lr: 1.00E-02, _patience: 3\n","Epoch: 21 | train_loss: 0.07147, val_loss: 0.03898, lr: 1.00E-02, _patience: 3\n","Epoch: 22 | train_loss: 0.07674, val_loss: 0.03809, lr: 1.00E-02, _patience: 3\n","Epoch: 23 | train_loss: 0.07691, val_loss: 0.03757, lr: 1.00E-02, _patience: 3\n","Epoch: 24 | train_loss: 0.07157, val_loss: 0.03968, lr: 1.00E-02, _patience: 2\n","Epoch: 25 | train_loss: 0.06961, val_loss: 0.03514, lr: 1.00E-02, _patience: 3\n","Epoch: 26 | train_loss: 0.06829, val_loss: 0.03487, lr: 1.00E-02, _patience: 3\n","Epoch: 27 | train_loss: 0.06388, val_loss: 0.03404, lr: 1.00E-02, _patience: 3\n","Epoch: 28 | train_loss: 0.06143, val_loss: 0.03264, lr: 1.00E-02, _patience: 3\n","Epoch: 29 | train_loss: 0.05946, val_loss: 0.03062, lr: 1.00E-02, _patience: 3\n","Epoch: 30 | train_loss: 0.06148, val_loss: 0.02967, lr: 1.00E-02, _patience: 3\n","Epoch: 31 | train_loss: 0.05410, val_loss: 0.02966, lr: 1.00E-02, _patience: 3\n","Epoch: 32 | train_loss: 0.06993, val_loss: 0.02909, lr: 1.00E-02, _patience: 3\n","Epoch: 33 | train_loss: 0.05992, val_loss: 0.02787, lr: 1.00E-02, _patience: 3\n","Epoch: 34 | train_loss: 0.05394, val_loss: 0.02756, lr: 1.00E-02, _patience: 3\n","Epoch: 35 | train_loss: 0.05170, val_loss: 0.02715, lr: 1.00E-02, _patience: 3\n","Epoch: 36 | train_loss: 0.05243, val_loss: 0.02694, lr: 1.00E-02, _patience: 3\n","Epoch: 37 | train_loss: 0.05663, val_loss: 0.02513, lr: 1.00E-02, _patience: 3\n","Epoch: 38 | train_loss: 0.04740, val_loss: 0.02436, lr: 1.00E-02, _patience: 3\n","Epoch: 39 | train_loss: 0.05591, val_loss: 0.02441, lr: 1.00E-02, _patience: 2\n","Epoch: 40 | train_loss: 0.05489, val_loss: 0.02437, lr: 1.00E-02, _patience: 1\n","Epoch: 41 | train_loss: 0.05202, val_loss: 0.02377, lr: 1.00E-02, _patience: 3\n","Epoch: 42 | train_loss: 0.05307, val_loss: 0.02533, lr: 1.00E-02, _patience: 2\n","Epoch: 43 | train_loss: 0.05089, val_loss: 0.02275, lr: 1.00E-02, _patience: 3\n","Epoch: 44 | train_loss: 0.04828, val_loss: 0.02315, lr: 1.00E-02, _patience: 2\n","Epoch: 45 | train_loss: 0.04679, val_loss: 0.02158, lr: 1.00E-02, _patience: 3\n","Epoch: 46 | train_loss: 0.04391, val_loss: 0.02220, lr: 1.00E-02, _patience: 2\n","Epoch: 47 | train_loss: 0.04147, val_loss: 0.02082, lr: 1.00E-02, _patience: 3\n","Epoch: 48 | train_loss: 0.04100, val_loss: 0.02100, lr: 1.00E-02, _patience: 2\n","Epoch: 49 | train_loss: 0.04155, val_loss: 0.02008, lr: 1.00E-02, _patience: 3\n","Epoch: 50 | train_loss: 0.05295, val_loss: 0.02094, lr: 1.00E-02, _patience: 2\n","Epoch: 51 | train_loss: 0.04619, val_loss: 0.02179, lr: 1.00E-02, _patience: 1\n","Stopping early!\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"HnjdSO1lxNq6"},"source":["#Evaluation"]},{"cell_type":"code","metadata":{"id":"UOsjjwfMxwUi"},"source":["import json\n","from sklearn.metrics import precision_recall_fscore_support"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"WUD_Krp-xrdl"},"source":["def get_performance(y_true, y_pred, classes):\n","    \"\"\"Per-class performance metrics.\"\"\"\n","    # Performance\n","    performance = {\"overall\": {}, \"class\": {}}\n","\n","    # Overall performance\n","    metrics = precision_recall_fscore_support(y_true, y_pred, average=\"weighted\")\n","    performance[\"overall\"][\"precision\"] = metrics[0]\n","    performance[\"overall\"][\"recall\"] = metrics[1]\n","    performance[\"overall\"][\"f1\"] = metrics[2]\n","    performance[\"overall\"][\"num_samples\"] = np.float64(len(y_true))\n","\n","    # Per-class performance\n","    metrics = precision_recall_fscore_support(y_true, y_pred, average=None)\n","    for i in range(len(classes)):\n","        performance[\"class\"][classes[i]] = {\n","            \"precision\": metrics[0][i],\n","            \"recall\": metrics[1][i],\n","            \"f1\": metrics[2][i],\n","            \"num_samples\": np.float64(metrics[3][i]),\n","        }\n","\n","    return performance"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"m8lXcBh9xPZ0"},"source":["# Get predictions\n","test_loss, y_true, y_prob = trainer.eval_step(dataloader=test_dataloader)\n","y_pred = np.argmax(y_prob, axis=1)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"u9exAxINxPgA","executionInfo":{"status":"ok","timestamp":1608245062106,"user_tz":420,"elapsed":728,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"aaf5143d-3f0e-4d22-f5e6-20d0afc0ef38"},"source":["# Determine performance\n","performance = get_performance(\n","    y_true=y_test, y_pred=y_pred, classes=label_encoder.classes)\n","print (json.dumps(performance['overall'], indent=2))"],"execution_count":null,"outputs":[{"output_type":"stream","text":["{\n","  \"precision\": 0.9956140350877193,\n","  \"recall\": 0.9955555555555556,\n","  \"f1\": 0.9955553580159119,\n","  \"num_samples\": 225.0\n","}\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"5PDPRJudCm8j"},"source":["# Saving and loading"]},{"cell_type":"markdown","metadata":{"id":"me91eCz3Cpr8"},"source":["Many tutorials never show you how to save the components you created so you can load them for inference."]},{"cell_type":"code","metadata":{"id":"OHz0RI7PCpw-"},"source":["from pathlib import Path"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"6-ouIfZjCpzv"},"source":["# Save artifacts\n","dir = Path(\"mlp\")\n","dir.mkdir(parents=True, exist_ok=True)\n","label_encoder.save(fp=Path(dir, 'label_encoder.json'))\n","X_scaler.save(fp=Path(dir, 'X_scaler.json'))\n","torch.save(best_model.state_dict(), Path(dir, 'model.pt'))\n","with open(Path(dir, 'performance.json'), \"w\") as fp:\n","    json.dump(performance, indent=2, sort_keys=False, fp=fp)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"UrGC_3RhCp2H","executionInfo":{"status":"ok","timestamp":1608245062108,"user_tz":420,"elapsed":706,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"eaa4d21e-1b91-497a-81e5-74330f78f0ad"},"source":["# Load artifacts\n","device = torch.device(\"cpu\")\n","label_encoder = LabelEncoder.load(fp=Path(dir, 'label_encoder.json'))\n","X_scaler = StandardScaler.load(fp=Path(dir, 'X_scaler.json'))\n","model = MLP(\n","    input_dim=INPUT_DIM, hidden_dim=HIDDEN_DIM, \n","    dropout_p=DROPOUT_P, num_classes=NUM_CLASSES)\n","model.load_state_dict(torch.load(Path(dir, 'model.pt'), map_location=device))\n","model.to(device)"],"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["MLP(\n","  (fc1): Linear(in_features=2, out_features=100, bias=True)\n","  (dropout): Dropout(p=0.1, inplace=False)\n","  (fc2): Linear(in_features=100, out_features=3, bias=True)\n",")"]},"metadata":{"tags":[]},"execution_count":83}]},{"cell_type":"code","metadata":{"id":"xbS5LmSgCqUn"},"source":["# Initialize trainer\n","trainer = Trainer(model=model, device=device)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"id":"wS-suIKQzwNI"},"source":["# Dataloader\n","sample = [[0.106737, 0.114197]] # c1\n","X = X_scaler.scale(sample)\n","y_filler = label_encoder.encode([label_encoder.classes[0]]*len(X))\n","dataset = Dataset(X=X, y=y_filler)\n","dataloader = dataset.create_dataloader(batch_size=batch_size)"],"execution_count":null,"outputs":[]},{"cell_type":"code","metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"6fK3_WcKzwQH","executionInfo":{"status":"ok","timestamp":1608245062334,"user_tz":420,"elapsed":910,"user":{"displayName":"Goku Mohandas","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GjMIOf3R_zwS_zZx4ZyPMtQe0lOkGpPOEUEKWpM7g=s64","userId":"00378334517810298963"}},"outputId":"ac8f952b-8d03-49a6-9074-649e43436b9e"},"source":["# Inference\n","y_prob = trainer.predict_step(dataloader)\n","y_pred = np.argmax(y_prob, axis=1)\n","label_encoder.decode(y_pred)"],"execution_count":null,"outputs":[{"output_type":"execute_result","data":{"text/plain":["['c1']"]},"metadata":{"tags":[]},"execution_count":86}]},{"cell_type":"markdown","metadata":{"id":"oUoRlWCyC2KA"},"source":["# Miscellaneous"]},{"cell_type":"markdown","metadata":{"id":"A4-8e9UIC38l"},"source":["There are lots of other utilities to cover as well such as:\n","\n","- Tokenizers to convert text to sequence of indices\n","- Various encoders to represent our data\n","- Padding to ensure uniform data shapes\n","- Experiment tracking to visualize and keep track of all experiments\n","- Hyperparameter optimization to tune our parameters (hidden units, learning rate, etc.)\n","- and many more!\n","\n","We'll explore these as we require them in future lessons including some in our [MLOps](https://madewithml.com/#mlops) course!\n","\n"]}]}